package ua.kpi.ipsa.expert.api.server;

import java.io.*;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;

import ua.kpi.ipsa.expert.api.*;
import ua.kpi.ipsa.expert.api.algorithm.*;
import ua.kpi.ipsa.expert.api.db.*;
import ua.kpi.ipsa.expert.api.net.*;

import com.sleepycat.je.*;

/**
 * 
 * @author doctorpro
 * 
 */
public final class ServerMain {

	// name in rmiregistry for the remote gateway object
	public static final String REMOTE_GATEWAY_NAME = "api_gateway";
	public static final int SERVER_PORT = 1099;

	//
	private ServerGateway serverGateway;

	//
	private Properties props;

	//
	private Environment env;

	//
	private DAO dao;

	//
	private LifecycleManager lifeManager;

	// retranslators
	private UserActionRetranslator userActionRetranslator;

	//
	private AlgoRunManager algoRunManager;

	private CodePresentationManager presentationManager;

	/**
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		ServerMain app = new ServerMain();

		// app.initSubsystem1();
		// app.initSubsystem2();

		// the primeval singularity point
		app.initProps();
		app.initLifeManager();

		// init dao (here we have a begin of time)
		app.initDao();

		// int algo manager
		app.initAlgoManager();

		// TODO: temporary: add algorithm stubs
		app.addAlgoStubs();

		//
		;

		// init remote objects
		app.initRemoteObjects();
	}

	/**
	 * 
	 */
	private ServerMain() {

		// create retranslators
		userActionRetranslator = new UserActionRetranslator();
	}

	/**
	 * 
	 */
	private void addAlgoStubs() {

		addAlgoStub(ua.kpi.ipsa.expert.api.algorithm.SpaceIgnorer.class
					.getName());
		addAlgoStub(ua.kpi.ipsa.expert.api.algorithm.ColmogorovZipper.class
				.getName());
		addAlgoStub(ua.kpi.ipsa.expert.api.algorithm.HaskellAlgo.class
						.getName());
		addAlgoStub(ua.kpi.ipsa.expert.api.algorithm.HashSifting.class
				.getName());
		addAlgoStub(ua.kpi.ipsa.expert.api.algorithm.HashSifting.class
				.getName());
	}
	
	private void addAlgoStub(String className) {
		try {
			// add space ignorer
			AlgorithmTuple at = new AlgorithmTuple();
			at.className = className;
			boolean contains = false;
			for (AlgorithmTuple a : dao.getAlgorithms()) {

				if (a.className.equals(at.className)) {
					contains = true;
					break;
				}
			}

			if (!contains) {
				dao.setAlgorithm(at);
				System.out.println("added algo: " + className);
			} else {
				System.out.println("already contains algo: " + className);
			}
		} catch (DataAccessException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * 
	 */
	private void initLifeManager() {
		lifeManager = new LifeManagerImpl();
	}

	/**
	 * 
	 */
	private void initAlgoManager() {
		System.out.println("Starting algo manager init  ...");
		presentationManager = new CodePresentationManager(dao, env);
		algoRunManager = new AlgoRunManager(dao, presentationManager);
		System.out.println("Algo manager inited [OK]");
	}

	/**
	 * 
	 */
	private void initProps() {
		System.out.println("Starting properties init  ...");
		try {
			props = new Properties();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Properties inited [OK]");
	}

	/**
	 * 
	 */
	private void initDao() {
		File dbPath = new File(props.get(Properties.DATABASE_PATH));
		try {
			env = BDBDAO.getEnvironment(dbPath);
			dao = new BDBDAO(env);
			// TODO WRONG EXCEPTION HANDLING!!!!
			// TODO not only here
		} catch (DataAccessException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * Method inits remote objects and adds them into the rmiregistry
	 * 
	 * inits serverGateway
	 * 
	 * @throws RemoteException
	 * 
	 */
	private void initRemoteObjects() {

		try {

			System.out.println("Starting server rmiregistry ...");
			LocateRegistry.createRegistry(SERVER_PORT);
			System.out.println("rmiregistry started successfully [OK]");

			// init server gateway
			System.out.println("Starting server gateway ...");

			serverGateway = new UserConsoleManager(userActionRetranslator, dao,
					algoRunManager, presentationManager, lifeManager);
			ServerGateway gatewayStub = (ServerGateway) UnicastRemoteObject
					.exportObject(serverGateway, 0);
			Registry registry = LocateRegistry.getRegistry();
			registry.rebind(REMOTE_GATEWAY_NAME, gatewayStub);

			System.out.println("Server gateway inited successfully [OK]");
		} catch (RemoteException ex) {
			System.out.println("Exception in initing server gateway: "
					+ ex.getLocalizedMessage());
			ex.printStackTrace();
		}
	}

	/**
	 * 
	 * @author doctorpro
	 * 
	 */
	private class LifeManagerImpl implements LifecycleManager {

		@Override
		public void shutdown() {

			// shuttingDown = true;

			new Thread() {
				@Override
				public void run() {
					try {

						// terminate all processes
						;

						// store props
						props.store();

						presentationManager.close();

						// close dao
						dao.close();

						// close env after dao!!!
						env.close();

						//
						System.exit(0);
					} catch (DataAccessException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}
